#include "map.h"

Map::Map(int size, int numberOfExits) : _mapSize(size+2),
                                        _numberOfExits(numberOfExits)
{
    qDebug() << "Enter:Map:Map";
    qsrand(QTime::currentTime().msec());
    /*****************************************/
    _plane = new MapField*[_mapSize];
    for(int j=0 ; j < _mapSize+2 ; j++)
    {
        _plane[j] = new MapField[_mapSize];
    }
    /*****************************************/

    /* order of method invocation is important */
    buildWalls();
    buildExits();
    drawExits();
    showOnConsole();
    qDebug() << "Leave:Map:Map";
}

Map::~Map()
{
    for(int i = 0 ; i < _mapSize ; i++)
       delete _plane[i];
    delete [] _plane;
}

void Map::showOnConsole()
{
    qDebug() << "Enter:Map:showOnConsole";
    QString q;
    for(int i=0 ; i < _mapSize ; i++ )
    {
        if(i>0)
            q.append("\n ");
        for(int j=0; j < _mapSize; j++ )
        {
            if(_plane[i][j].type == WALL)
            {
                q.append("[*]");
            }
            else if(_plane[i][j].type == DOOR_OPEN)
            {
                q.append("[O]");
            }
            else if(_plane[i][j].type == DOOR_CLOSED)
            {
                q.append("[C]");
            }
            else
            {
                q.append("[  ]");
            }
        }
    }

    qDebug() << q.toAscii();
    qDebug() << "Leave:Map:showOnConsole";
}

void Map::buildWalls()
{

    for(int i=0;i < _mapSize; i++)
    {
        _plane[0][i].type = WALL;
        _plane[_mapSize-1][i].type = WALL;

        _plane[i][0].type = WALL;
        _plane[i][_mapSize-1].type = WALL;
    }

    int i = 0;

    while(i < _mapSize * 20)
    {
        QPoint p1 = generateRandomCoordinateOnMap();
        QPoint p2 = generateRandomCoordinateOnMap();

        //avoid straight lines
        if((p1.x() - p2.x())== 0)
            continue;
        QLinkedList<QPoint> listOfPoints = Wall::buildWall(p1, p2);

        if(!validateWall(listOfPoints))
        {
            i++;
            continue;
        }

        putWallOnMap(listOfPoints);
    }
}

QPoint Map::generateRandomCoordinateOnMap()
{

    int x   = qrand()% (_mapSize-1);
    int y   = qrand()% (_mapSize-1);
    x+=1;
    y+=1;
    QPoint point(x,y);
    return point;
}

void Map::putWallOnMap(QLinkedList<QPoint> wall)
{
    foreach(const QPoint &pt, wall)
    {
        _plane[pt.x()][pt.y()].type = WALL;

        _plane[pt.x()+1][pt.y()+1].setPoison(true);
        _plane[pt.x()][pt.y()+1].setPoison(true);
        _plane[pt.x()+1][pt.y()].setPoison(true);

        _plane[pt.x()-1][pt.y()-1].setPoison(true);
        _plane[pt.x()][pt.y()-1].setPoison(true);
        _plane[pt.x()-1][pt.y()].setPoison(true);

        _plane[pt.x()-1][pt.y()+1].setPoison(true);
        _plane[pt.x()+1][pt.y()-1].setPoison(true);

        _plane[pt.x()][pt.y()].type = WALL;
    }
}

bool Map::validateWall(QLinkedList<QPoint> wall)
{
    bool isconnectedToSurroundingWall  = false;

    foreach(const QPoint &pt, wall)
    {
        if(_plane[pt.x()][pt.y()].isPoison())
            return false;

        bool check = (pt.x() == 1) ||
                     (pt.x() == _mapSize-2) ||
                     (pt.y() == 1) ||
                     (pt.y() == _mapSize-2);

        if(check)
        {
            if(isconnectedToSurroundingWall == true)
                return false;
            else
                isconnectedToSurroundingWall = true;
        }
    }
    return true;
}

void Map::buildExits()
{
    for(int i=0 ; i < _numberOfExits ; i++)
    {
        int wallNr = (qrand()%4);
        QPoint randomPoint = generateRandomCoordinateOnMap();


        /* avoid exits in the corners */
        if(randomPoint.y() == 0)
            ++randomPoint.ry();
        if(randomPoint.y() == (_mapSize-1))
            --randomPoint.ry();
        if(randomPoint.x() == 0)
            ++randomPoint.rx();
        if(randomPoint.x() == (_mapSize-1))
            --randomPoint.rx();


        Exit exit(randomPoint.x(), randomPoint.y());
        //exit.drawClosed();
        switch(wallNr)
        {
        case NORTH:
            exit.setY(0);
            break;
        case SOUTH:
            exit.setY(_mapSize-1);
            break;
        case EAST:
            exit.setX(0);
            break;
        case WEST:
            exit.setX(_mapSize-1);
            break;
        default:
            Q_ASSERT(0);
        }
        _exits.append(exit);
    }
}

void Map::drawExits()
{
   bool thereIsOpenExit = false;
   while(!thereIsOpenExit)
   {
       foreach(Exit e, _exits)
       {
            e.drawClosed();
            if(e.isClosed())
            {
                _plane[e.x()][e.y()].type = DOOR_CLOSED;
            }
            else
            {
                _plane[e.x()][e.y()].type = DOOR_OPEN;
                thereIsOpenExit = true;
            }
       }
    }
}

int Map::getMapSize() const
{
    return _mapSize;
}

MapField& Map::operator()(int x, int y)
{
    return _plane[x][y];
}

//double  operator() (unsigned row, unsigned col) const;
